home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
cmln0286.arc
/
XMODEM.S
< prev
Wrap
Text File
|
1986-02-03
|
6KB
|
273 lines
Taken from Kent Williams's
State Machines in C article
February 1986 C issue
XMODEM.S
/*
* xmodem.s
* implementation of xmodem protocol
* source for state machine compiler statecom
*/
#include <stdio.h>ì
#include <fcntl.h>ì
#define TIMEOUT 0xFFFFì
#define TICKSPERSEC 18ì
#define SECSIZ 128ì
#define RETRYMAX 10ì
#define SOH 1ì
#define EOT 4ì
#define ACK 6ì
#define NAK 21ì
#define SECSIZ 128
typedef struct {
unsigned char soh;
unsigned char sectnum;
unsigned char sectcomp;
char buffer[SECSIZ];
unsigned char checksum;ì
} packet;
typedef char sector[128];
extern timeout_read(); /* returns character if received, else -1 */ì
extern async_write(); /* writes a buffer to async port */ìèextern async_putc(); /* writes one character to async port */
$machine xsend init (file) char *file;
/* machine global variables */ì
int sectnum,attempts,fd;ì
unsigned testchar;ì
packet block;
$state init
attempts = 0;
if (-1 == (fd = open(file, O_RDONLY)))
{
fprintf(stderr,"can't open %s\n",file);
$nextstate error
}
fprintf(stderr,"Waiting for initial NAK ... attempt ");
$nextstate waitfornakì
$endstate init
$state waitfornak
if (attempts > RETRYMAX)
{
fprintf(stderr,"none recieved - aborting\n");
$nextstate error
}
if (NAK != (testchar = timeout_read(5)))
{
++attempts;
printf("# %d ",attempts);
$nextstate waitfornak
}
fprintf(stderr,"\nreceived!\n");
attempts = 0;
sectnum = 1;
$nextstate getpacket ì
$endstate waitfornak
$state getpacket
register int actual_read,i;
unsigned char checksum;
block.soh = SOH;
block.sectnum = (char) sectnum;
block.sectcomp = (char) ~sectnum;
switch(actual_read = read(fd,&(block.buffer[0]),SECSIZ))
{
case SECSIZ:
break; /* everything's ok */
case 0:
$nextstate endoffile
default:
/* fractional sector */
for( ; actual_read < SECSIZ; actual_read++)
block.buffer[actual_read] = 0;è break;
}
for (i = 0,checksum = 0; i < SECSIZ; i++)
checksum = 0xFF & (checksum + block.buffer[i]);
block.checksum = checksum;
$nextstate sendpacketì
$endstate getpacket
$state sendpacket
if (attempts >= RETRYMAX)
{
fprintf(stderr,"too many retries - aborting\n");
$nextstate error
}
else
{
fprintf(stderr,"sending sector %d\r",sectnum);
async_write(&block,sizeof(packet));
$nextstate waitforack
}ì
$endstate sendpacket
$state waitforack
if (ACK == timeout_read(4))
{
sectnum++;
attempts = 0;
$nextstate getpacket
} else
{
attempts++;
$nextstate sendpacket
}ì
$endstate waitforack
$state endoffile
if (attempts > RETRYMAX)
{
fprintf(stderr,"no acknowledgement for end of file\n");
$nextstate error
}
async_putc(EOT);
if(ACK != (testchar = timeout_read(2)))
{
++attempts;
$nextstate endoffile
}
fprintf(stderr,"end of file acknowledged\n");
$nextstate done ì
$endstate endoffile
$state error
fprintf(stderr,"error encountered in file transmission\n");
close(fd);
$nextstate doneìè$endstate error
$state done
close(fd);
$nextstate terminalì
$endstate done
$endmachine xsend
$machine xreceive rinit (file) char *file;
/* uses global variables in common with send */ì
int sectnum,attempts,fd;ì
unsigned testchar;ì
sector *sp;
$state rinit
if (-1 == (fd = creat(file,0666)))
{
fprintf(stderr,"can't create %s\n",file);
return -1;
}
attempts = 0;
sectnum = 1; /* start at one */
fprintf(stderr,"sending NAK ");
$nexstate sendnakì
$endstate rinit
$state sendnak
if (attempts >= RETRYMAX)
{
fprintf(stderr,"aborting ...\n");
$nextstate rerror
}
fprintf(stderr,"#%d ",attempts);
/* send naks until soh */
async_putc(NAK);
if (SOH != (testchar = timeout_read(10)))
{
++attempts;
$nextstate sendnak
}
attempts = 0;
$nextstate getsector ì
$endstate sendnak
$state getsector
static sector buf;
int testchar;
register unsigned int checksum;
register int i;
if (attempts >= RETRYMAX)
$nextstate rerror
fprintf(stderr,"receiving sector %d\r",sectnum);è if ((testchar = timeout_read(3)) != sectnum)
{
fprintf(stderr,"\nexpected sector %d got %d\n",sectnum,testchar);
++attempts;
$nextstate waitforsilence
}
if ((testchar = timeout_read(3)) != (0xFF & (~sectnum)))
{
fprintf(stderr,"\nsector complement error, expected %d, got %d\n",
testchar,~sectnum & 0xFF);
++attempts;
$nextstate waitforsilence
}
/* get a block */
checksum = 0;
for (i = 0;i < SECSIZ; i++)
{
checksum = 0xFF & (checksum + (buf[i] = timeout_read(3)));
}
if (checksum != (testchar = timeout_read(3)))
{
fprintf(stderr,"\nchecksum error\n,expected %d, got %d",
checksum,testchar);
attempts++;
async_putc(NAK);
$nextstate waitforsoh
}
write(fd,&buf,sizeof(sector));
async_putc(ACK);
attempts = 0;
sectnum++;
$nextstate waitforsohì
$endstate getsector
$state waitforsoh
if (attempts >= RETRYMAX)
$nextstate rerror
switch (timeout_read(4))
{
case SOH:
$nextstate getsector
case TIMEOUT:
attempts++;
fprintf(stderr,"\ntimeout on SOH\n");
$nextstate waitforsoh
case EOT:
async_putc(ACK);
fprintf(stderr,"\nend of file received\n");
$nextstate rdone
default:
attempts++;
$nextstate waitforsilence
}ì
$endstate waitforsoh
è$state waitforsilence
/* re-sync between sectors */
do
{
}
while (TIMEOUT != timeout_read(1));
$nextstate waitforsohì
$endstate waitforsilence
$state rerror
fprintf(stderr,"Error receiving file\n");
$nextstate rdoneì
$endstate rerror
$state rdone
close(fd);
$nextstate terminalì
$endstate rdone
$endmachine xreceive